/*
 * ring_buffer_dma.c
 *
 *  Created on: 2024年8月16日
 *      Author: nwz
 */
#include "ring_buffer_dma.h"
#define MAX_DMA_COPY_WAIT_TICKS pdMS_TO_TICKS(1000)
static uint32_t get_copy1_read_size(uint32_t start_pos, uint32_t end_pos,
		uint32_t buffer_size) {
	if (end_pos > start_pos) {
		return end_pos - start_pos;
	} else if (end_pos < start_pos) {
		return buffer_size - start_pos;
	}
	return 0;
}

static uint32_t get_copy2_read_size(uint32_t start_pos, uint32_t end_pos,
		uint32_t buffer_size) {
	if (end_pos < start_pos) {
		return end_pos;
	}
	return 0;
}

uint32_t ring_buffer_get_readable_size(ring_buffer_handle_t handle) {
	uint32_t start_pos = handle->start_pos;
	uint32_t end_pos = handle->end_pos;
	uint32_t buffer_size = handle->buffer_size;
	return get_copy1_read_size(start_pos, end_pos, buffer_size)
			+ get_copy2_read_size(start_pos, end_pos, buffer_size);
}

static uint32_t get_copy1_write_size(uint32_t start_pos, uint32_t end_pos,
		uint32_t buffer_size) {
	if (end_pos > start_pos) {
		if (start_pos == 0) {
			return buffer_size - end_pos - 1;
		} else {
			return buffer_size - end_pos;
		}
	} else if (end_pos < start_pos) {
		return start_pos - end_pos - 1;
	} else {
		return buffer_size - end_pos - (start_pos == 0 ? 1 : 0);
	}
	return 0;
}

static uint32_t get_copy2_write_size(uint32_t start_pos, uint32_t end_pos,
		uint32_t buffer_size) {
	if (end_pos > start_pos) {
		if (start_pos > 0) {
			return start_pos-1;
		}
	} else if (end_pos == start_pos) {
		if (end_pos != 0) {
			return end_pos - 1;
		}
	}
	return 0;
}

ring_buffer_handle_t ring_buffer_create(uint32_t size, void *write_channel,
		void *read_channel) {
	ring_buffer_handle_t handle = pvPortMalloc(sizeof(ring_buffer_handle));
	ASSERT(handle);
	MEM_ZERO(*handle);

	handle->buffer = pvPortMalloc(size);
	ASSERT(handle->buffer);

	handle->sem = xSemaphoreCreateBinary();
	ASSERT(handle->sem);
	handle->write_sem = xSemaphoreCreateBinary();
	ASSERT(handle->write_sem);
	handle->read_sem = xSemaphoreCreateBinary();
	ASSERT(handle->read_sem);

	handle->buffer_size = size;
	handle->write_channel = write_channel;
	handle->read_channel = read_channel;
	return handle;
}

uint32_t ring_buffer_read(ring_buffer_handle_t handle, uint8_t *data,
		uint32_t size, uint32_t wait_ticks) {
	uint32_t ret = 0;

	xSemaphoreTake(handle->sem, 0);
	if (size > 0
			&& (ring_buffer_get_readable_size(handle) > 0
					|| xSemaphoreTake(handle->sem,wait_ticks) == pdTRUE)) {
		uint32_t end_pos = handle->end_pos;
		// copy data
		uint32_t copy1 = get_copy1_read_size(handle->start_pos, end_pos,
				handle->buffer_size);
		uint32_t copy2 = get_copy2_read_size(handle->start_pos, end_pos,
				handle->buffer_size);

		copy1 = min(copy1, size);
		copy2 = min(copy2, size - copy1);

		// copy
		if (copy1 > 0) {
			if (copy1 >= MIN_DMA_TRANSFER_SIZE) {
				ASSERT(
						HAL_DMA_Start_IT(handle->read_channel,
								(uint32_t )handle->buffer + handle->start_pos,
								(uint32_t )data, copy1) == HAL_OK);
				ASSERT(
						xSemaphoreTake(handle->read_sem,MAX_DMA_COPY_WAIT_TICKS) == pdTRUE);
			} else {
				memcpy(data, handle->buffer + handle->start_pos, copy1);
			}
		}

		if (copy2 > 0) {
			if (copy2 >= MIN_DMA_TRANSFER_SIZE) {
				ASSERT(
						HAL_DMA_Start_IT(handle->read_channel,
								(uint32_t )handle->buffer,
								(uint32_t )data + copy1, copy2) == HAL_OK);
				ASSERT(
						xSemaphoreTake(handle->read_sem,MAX_DMA_COPY_WAIT_TICKS) == pdTRUE);
			} else {
				memcpy(data + copy1, handle->buffer, copy2);
			}
		}

		handle->start_pos = (handle->start_pos + copy1 + copy2)
				% handle->buffer_size;

		ret = copy1 + copy2;
	}
	return ret;
}

uint32_t ring_buffer_write_detail(ring_buffer_handle_t handle, uint8_t *data,
		uint32_t size, bool fromISR) {
	uint32_t ret = 0;
	if (data && size > 0) {
		uint32_t start_pos = handle->start_pos;
		uint32_t copy1 = get_copy1_write_size(start_pos, handle->end_pos,
				handle->buffer_size);
		uint32_t copy2 = get_copy2_write_size(start_pos, handle->end_pos,
				handle->buffer_size);

		copy1 = min(copy1, size);
		copy2 = min(copy2, size - copy1);

		// copy
		if (copy1 > 0) {
			if (copy1 >= MIN_DMA_TRANSFER_SIZE && !fromISR) {
				ASSERT(
						HAL_DMA_Start_IT(handle->write_channel, (uint32_t )data,
								(uint32_t )handle->buffer + handle->end_pos,
								copy1) == HAL_OK);
				ASSERT(
						xSemaphoreTake(handle->write_sem,MAX_DMA_COPY_WAIT_TICKS) == pdTRUE);
			} else {
				memcpy(handle->buffer + handle->end_pos, data, copy1);
			}
		}

		if (copy2 > 0) {
			if (copy2 >= MIN_DMA_TRANSFER_SIZE && !fromISR) {
				ASSERT(
						HAL_DMA_Start_IT(handle->write_channel,
								(uint32_t )data + copy1,
								(uint32_t )handle->buffer, copy2) == HAL_OK);
				ASSERT(
						xSemaphoreTake(handle->write_sem,MAX_DMA_COPY_WAIT_TICKS) == pdTRUE);
			} else {
				memcpy(handle->buffer, data + copy1, copy2);
			}
		}

		handle->end_pos = (handle->end_pos + copy1 + copy2)
				% handle->buffer_size;

		if (fromISR) {
			BaseType_t val = pdFALSE;
			xSemaphoreGiveFromISR(handle->sem, &val);
		} else {
			xSemaphoreGive(handle->sem);
		}

		ret = copy1 + copy2;
	}
	return ret;
}

void ring_buffer_clear(ring_buffer_handle_t handle) {
	handle->start_pos = handle->end_pos;
}

void ring_buffer_write_cmplt_callback(ring_buffer_handle_t handle, void *dma) {
	BaseType_t value = pdFALSE;
	ASSERT(dma == handle->write_channel);
	ASSERT(xSemaphoreGiveFromISR(handle->write_sem,&value) == pdTRUE);
}

void ring_buffer_read_cmplt_callback(ring_buffer_handle_t handle, void *dma) {
	BaseType_t value = pdFALSE;
	ASSERT(dma == handle->read_channel);
	ASSERT(xSemaphoreGiveFromISR(handle->read_sem,&value) == pdTRUE);
}

uint32_t ring_buffer_get_available_size(ring_buffer_handle_t handle) {
	uint32_t start_pos = handle->start_pos;
	uint32_t end_pos = handle->end_pos;
	uint32_t buffer_size = handle->buffer_size;
	return get_copy1_write_size(start_pos, end_pos, buffer_size)
			+ get_copy2_write_size(start_pos, end_pos, buffer_size);
}

uint32_t ring_buffer_delete(ring_buffer_handle_t handle) {
	ASSERT(0);
}
